package services;

import java.io.IOException;
import java.lang.reflect.Field;
import java.net.MalformedURLException;
import java.net.URL;
import java.text.DateFormat;
import java.text.ParseException;
import java.text.SimpleDateFormat;
import java.util.ArrayList;
import java.util.Date;
import java.util.List;
import java.util.TreeMap;

import org.apache.tools.ant.Main;

import objects.AnalyticDataSource;
import objects.AnalyticEntryRow;
import objects.ProfileAnalytic;

import models.UserModel;

import com.google.gdata.client.analytics.AnalyticsService;
import com.google.gdata.client.analytics.DataQuery;
import com.google.gdata.data.analytics.AccountEntry;
import com.google.gdata.data.analytics.AccountFeed;
import com.google.gdata.data.analytics.Aggregates;
import com.google.gdata.data.analytics.DataEntry;
import com.google.gdata.data.analytics.DataFeed;
import com.google.gdata.data.analytics.DataSource;
import com.google.gdata.data.analytics.Dimension;
import com.google.gdata.data.analytics.Metric;
import com.google.gdata.util.ServiceException;

import debugs.objects.AnalyticDataSourceDebug;
import debugs.objects.AnalyticEntryRowDebug;

public class GoogleAnalytics {
	
	private String APPLICATION_NAME = "New Site Info";
	
	private String accountUrl = "https://www.google.com/analytics/feeds/accounts/default";
	
	private String dataUrl = "https://www.google.com/analytics/feeds/data";
	
	private AnalyticsService as;
	
	private AnalyticDataSource dataSource;
	
	public GoogleAnalytics(UserModel user) {
		//------------------------------------------------------
	    // Configure GA API
	    //------------------------------------------------------
		as = new AnalyticsService(APPLICATION_NAME);
		//------------------------------------------------------
	    // Client Login Authentication
	    //------------------------------------------------------
		as.setAuthSubToken(user.getSessionToken());
		
		//Set analytic data source
		dataSource = new AnalyticDataSource();
	}
	
	
	public TreeMap getAccountFeed() {
		TreeMap result = new TreeMap();
		TreeMap<String, String> error = new TreeMap<String, String>();
		ArrayList<ProfileAnalytic> profiles = new ArrayList<ProfileAnalytic>();
		ProfileAnalytic profile = new ProfileAnalytic();
		//------------------------------------------------------
	    // GA Account Feed
	    //------------------------------------------------------
	    URL queryUrl = null;
	    try {
	      queryUrl = new URL(this.accountUrl);
	    } catch (MalformedURLException e) {
	      error.put("queryUrl", "Malformed URL: " + this.accountUrl);
	      //System.err.println("Malformed URL: " + this.accountUrl);
	    }

	    // Send our request to the Analytics API and wait for the results to come back
	    AccountFeed accountFeed = null;
	    try {
	        accountFeed = as.getFeed(queryUrl, AccountFeed.class);
	    } catch (IOException e) {
	        error.put("feedAccount", "Network error trying to retrieve feed: " + e.getMessage());
	    } catch (ServiceException e) {
	        error.put("apiResponded", "Analytics API responded with an error message: " + e.getMessage());
	    }
	    
	    // Print the feeds' entry data
	    for (AccountEntry entry : accountFeed.getEntries()) {
	    	profile = new ProfileAnalytic();
	    	profile.setWebPropertyId(entry.getProperty("ga:webPropertyId"));
	    	profile.setAccountName(entry.getProperty("ga:accountName"));
	    	profile.setAccountId(entry.getProperty("ga:accountId"));
	    	profile.setProfileName(entry.getTitle().getPlainText());
	    	profile.setProfileId(entry.getProperty("ga:profileId"));
	    	profile.setTableId(entry.getTableId().getValue());
	    	
	    	profiles.add(profile);
	    }
	    
	    result.put("error", error);
	    result.put("profiles", profiles);
	    
	    return result;
	}
	
	public AnalyticDataSource getDataFeed(String ids, String dimensions, String metrics, String startDate, String endDate, int maxResults, String sort) {
		TreeMap result = new TreeMap();
		TreeMap<String, String> error = new TreeMap<String, String>();
		ArrayList<ProfileAnalytic> profiles = new ArrayList<ProfileAnalytic>();
		
	    DataQuery query;
	    //------------------------------------------------------
	    // GA Data Feed
	    //------------------------------------------------------
	    // first build the query
	    try {
	      query = new DataQuery(new URL(this.dataUrl));
	    } catch (MalformedURLException e) {
	      System.err.println("Malformed URL: " + this.dataUrl);
	      return this.dataSource;
	    }
	    /*query.setIds("ga:" + profileId);
	    query.setDimensions("ga:date,ga:city");
	    query.setMetrics("ga:visits,ga:bounces");
	    query.setSort("-ga:visits");
	    //query.setFilters("ga:medium==referral");
	    query.setMaxResults(100);
	    query.setStartDate("2012-06-01");
	    query.setEndDate("2012-06-30");*/
	    query.setIds(ids);
	    query.setDimensions(dimensions);
	    query.setMetrics(metrics);
	    query.setSort(sort);
	    if (maxResults != 0) {
	    	query.setMaxResults(maxResults);
	    }
	    query.setStartDate(startDate);
	    query.setEndDate(endDate);
	    URL url = query.getUrl();

	    // Send our request to the Analytics API and wait for the results to come back
	    DataFeed feed;
	    try {
	      feed = as.getFeed(url, DataFeed.class);
	    } catch (IOException e) {
	      System.err.println("Network error trying to retrieve feed: " + e.getMessage());
	      return this.dataSource;
	    } catch (ServiceException e) {
	      System.err.println("Analytics API responded with an error message: " + e.getMessage());
	      return this.dataSource;
	    }

	    try {
	    	getFeedData(feed);
	    	getFeedDataSources(feed);
	    	getFeedAggregates(feed);
	    	getEntryRowData(feed);
		} catch (Exception e) {
			e.printStackTrace();
		}
		
		
		return this.dataSource;
	}
	
	//------------------------------------------------------
	// Format Feed Related Data
	//------------------------------------------------------
	/**
	 * Output the information specific to the feed.
	 * @param {DataFeed} feed Parameter passed
	 *     back from the feed handler.
	 * @throws IOException 
	 */
	private void getFeedData(DataFeed feed) {
		this.dataSource.setFeedTitle(feed.getTitle().getPlainText());
		this.dataSource.setFeedId(feed.getId());
		this.dataSource.setTotalResult(new Integer(feed.getTotalResults()));
		this.dataSource.setStartIndex(new Integer(feed.getStartIndex()));
		this.dataSource.setItemsPerPage(new Integer(feed.getItemsPerPage()));
		  
		DateFormat formatter = new SimpleDateFormat("yyyy-MM-dd");
		  
		try {  
			Date startDate = (Date) formatter.parse(feed.getStartDate().getValue());
			Date endDate = (Date) formatter.parse(feed.getEndDate().getValue());
			this.dataSource.setStartDate(startDate);
			this.dataSource.setEndDate(endDate);
		} catch (ParseException e) {
			System.out.println("Exception :" + e);  
		}  
	}
	
	/**
	 * Output information about the data sources in the feed.
	 * Note: the GA Export API currently has exactly one data source.
	 * @param {DataFeed} feed Parameter passed
	 *     back from the feed handler.
	 * @throws IOException 
	 */
	public void getFeedDataSources(DataFeed feed) {
		 DataSource gaDataSource = feed.getDataSources().get(0);
		 this.dataSource.setTableName(gaDataSource.getTableName().getValue());
		 this.dataSource.setTableId(gaDataSource.getTableId().getValue());
		 this.dataSource.setWebPropertyId(gaDataSource.getProperty("ga:webPropertyId"));
		 this.dataSource.setProfileId(gaDataSource.getProperty("ga:profileId"));
		 this.dataSource.setAccountName(gaDataSource.getProperty("ga:accountName"));
	}
	
	/**
	  * Output all the metric names and values of the aggregate data.
	  * The aggregate metrics represent values across all of the entries selected 
	  *     by the query and not just the rows returned.
	  * @param {DataFeed} feed Parameter passed
	  *     back from the feed handler.
	  * @throws IOException 
	  */
	public void getFeedAggregates(DataFeed feed) {  
	    
	    List<DataEntry> entries = feed.getEntries();
	    List<String> metrics = new ArrayList<String>();
	    List<String> dimensions = new ArrayList<String>();
	    
	    if (entries.size() != 0) {
	    	DataEntry singleEntry = entries.get(0);
	    	// iterate through all the dimensions
		    List<Dimension> ds = singleEntry.getDimensions();
		    for (Dimension dimension : ds) {
			    dimensions.add(dimension.getName().replaceAll(":", "_"));
		    }

		    // iterate through all the metrics
		    List<Metric> ms = singleEntry.getMetrics();
		    for (Metric metric : ms) {
		    	metrics.add(metric.getName().replaceAll(":", "_"));
		    }
		    
		    this.dataSource.setMetric(metrics);
		    this.dataSource.setDimension(dimensions);
	    }
	    
	}
	
	/**
	  * Output all the important information from the first entry in the data feed.
	  * @param {DataFeed} feed Parameter passed
	  *     back from the feed handler.
	  * @throws IOException 
	  */
	public void getEntryRowData(DataFeed feed) {
		List<AnalyticEntryRow> rows = new ArrayList<AnalyticEntryRow>();
		AnalyticEntryRow analyticRow = new AnalyticEntryRow();
		List<DataEntry> entries = feed.getEntries();
		
	    if (entries.size() != 0) {
	    	DataEntry singleEntry = entries.get(0);
		    List<Dimension> dimensions = singleEntry.getDimensions();
		    List<Metric> metrics = singleEntry.getMetrics();
		    List<String> feedDataNames = new ArrayList<String>();
		    String feedDataValues = "";

		    // put all the dimension and metric names into an array
		    for (Dimension dimension : dimensions) {
		      feedDataNames.add(dimension.getName());
		    }
		    for (Metric metric : metrics) {
		      feedDataNames.add(metric.getName());
		    }

		    // put the values of the dimension and metric names into the table
		    Field f;
		    for (DataEntry entry : entries) {
		    	analyticRow = new AnalyticEntryRow();
		        for (String dataName : feedDataNames) {
		    	   //feedDataValues += "\n" + dataName + "\t= " + entry.stringValueOf(dataName);
		    	   try {
		    		   f = analyticRow.getClass().getDeclaredField(dataName.replaceAll(":", "_"));
			    	   f.setAccessible(true);
			    	   f.set(analyticRow, entry.stringValueOf(dataName));
			  	   } catch (NoSuchFieldException e) {
			  		   //e.printStackTrace();
			   	   } catch (SecurityException e) {
			   		  //e.printStackTrace();
			  	   } catch (IllegalArgumentException e) {
					 // TODO Auto-generated catch block
					 //e.printStackTrace();
				   } catch (IllegalAccessException e) {
					 // TODO Auto-generated catch block
					 //e.printStackTrace();
				   }
		        }
		        rows.add(analyticRow);
		    }
		    
		    this.dataSource.setEntries(rows);
	    }
	}
}
